bitkeeper revision 1.682 (400c3301usqWi45kB_H-oz2aBEtHyw)
authorkaf24@scramble.cl.cam.ac.uk[kaf24] <kaf24@scramble.cl.cam.ac.uk[kaf24]>
Mon, 19 Jan 2004 19:41:53 +0000 (19:41 +0000)
committerkaf24@scramble.cl.cam.ac.uk[kaf24] <kaf24@scramble.cl.cam.ac.uk[kaf24]>
Mon, 19 Jan 2004 19:41:53 +0000 (19:41 +0000)
maw-vd-rc2.patch

.rootkeys
tools/examples/vd_delete.py
tools/examples/vd_freespace.py [new file with mode: 0644]
tools/examples/vd_list.py [new file with mode: 0644]
tools/examples/vd_refresh.py
tools/examples/vd_undelete.py [new file with mode: 0644]
tools/xc/py/XenoUtil.py

index a4fd7de8245e18980208470e31f1c645c15892e9..3178fdeae6fb0844f64206d55cb1688881a16085 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 40083bb4LeyQyL-0riaV3UYDfHkl5g tools/examples/vd_create.py
 40083bb4TmKs8pcFkOcJj1bKn3zcmg tools/examples/vd_delete.py
 40083bb4u9Od6ujgect6mrxWfkk1pQ tools/examples/vd_format.py
+400c33000SvWkdG92u4Bvdu6BPjGPw tools/examples/vd_freespace.py
+400c3300jb_Ufz2kWsovGKNoDPEf-A tools/examples/vd_list.py
 40083bb4NhDpKiYTrebI3ZjX__oI_w tools/examples/vd_refresh.py
+400c33001-uDKTfHBchTKUwuMFcqTA tools/examples/vd_undelete.py
 3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile
 3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile
 3f6dc140C8tAeBfroAF24VrmCS4v_w tools/misc/miniterm/README
index ad7a210b14d2728d138afc656cff5c73ecf1af7b..ca2a3fd19fdb3c307b81ebe8c56d4e7af3ae1319 100644 (file)
@@ -18,3 +18,6 @@ else:
 print "Deleting a virtual disk with ID: " + id
 
 ret = XenoUtil.vd_delete(id)
+
+if ret:
+    print "Deletion failed: invalid ID, or disk already expired / deleted"
diff --git a/tools/examples/vd_freespace.py b/tools/examples/vd_freespace.py
new file mode 100644 (file)
index 0000000..5ebc762
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+#
+# vd_freespace.py
+#
+# Prints out the amount of free space (in megabytes) for creating virtual disks.
+#
+
+import XenoUtil
+
+print XenoUtil.vd_freespace()
diff --git a/tools/examples/vd_list.py b/tools/examples/vd_list.py
new file mode 100644 (file)
index 0000000..6be8ccb
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+#
+# vd_list.py - use this to list all the unexpired virtual disks in the
+# virtual disk database
+#
+
+import XenoUtil
+
+print XenoUtil.vd_list()
index cfe7f4a2aa0495ef41e6efad389cc1b4e9661a2b..afbdfb8ea836a923c034844964793e9549197750 100644 (file)
@@ -29,3 +29,5 @@ print "Expiry time (seconds from now): " + sys.argv[2]
 
 ret = XenoUtil.vd_refresh(id, expiry_time)
 
+if ret:
+    print "Refresh failed, non-existent virtual disk or disk is expired / deleted"
diff --git a/tools/examples/vd_undelete.py b/tools/examples/vd_undelete.py
new file mode 100644 (file)
index 0000000..d43c773
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+#
+# vd_undelete.py vdisk_id [ new_expiry ]
+#
+# Undeletes a VD and, optionally, sets a new expiry time or disables
+# expiry if the time value is zero (default)
+#
+
+import XenoUtil, sys
+
+if len(sys.argv) < 2:
+    print >>sys.stderr, "Usage: " + sys.argv[0] + """ vdisk_id [ new_expiry ]
+    vdisk_id   - the identifier of the deleted VD
+    new_expiry - optionally the new expiry time in seconds from now (0
+                 for never expire - which is the default)
+
+    VDs can currently only be undeleted if it is safe to do so,
+    i.e. if none of their space has been reallocated.
+ """
+
+vdisk_id = sys.argv[1]
+
+if len(sys.argv) == 3:
+    expiry = int(sys.argv[2])
+else:
+    expiry = 0
+
+if XenoUtil.vd_undelete(vdisk_id, expiry):
+    print "Undelete operation failed for virtual disk: " + vdisk_id
+else:
+    print "Undelete operation succeeded for virtual disk: " + vdisk_id
index c37aaaf8f24ce27a0e642b9753c837d7e46e28c3..abf320dab5dc37cb9aef014dcd1d56a232be61b6 100644 (file)
@@ -219,6 +219,7 @@ def vd_format(partition, extent_size_mb):
     cx.close()
     return 0
 
+
 def vd_create(size_mb, expiry):
     """Create a new virtual disk.
     size_mb [int]: size in megabytes for the new virtual disk
@@ -252,10 +253,10 @@ def vd_create(size_mb, expiry):
 
     if expiry:
         expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
-        expires = 1;
+        expires = 1
     else:
         expiry_ts = "NULL"
-        expires = 0;
+        expires = 0
 
     # we'll use this to build the SQL statement we want
     building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \
@@ -269,6 +270,7 @@ def vd_create(size_mb, expiry):
         if not row:
             cx.close()
             return -1
+        
 
         (vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
         allocated += extent_size
@@ -310,6 +312,19 @@ def vd_lookup(id):
     cx = sqlite.connect(VD_DB_FILE)
     cu = cx.cursor()
 
+    cu.execute("-- types int")
+    cu.execute("""SELECT COUNT(*)
+                  FROM vdisks
+                  WHERE (expiry_time > datetime('now') OR NOT expires)
+                              AND vdisk_id = """ + id)
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return -1
+
+    cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id)
+    real_size, = cu.fetchone()
   
     # This query tells PySQLite how to convert the data returned from the
     # following query - the use of the multiplication confuses it otherwise ;-)
@@ -326,11 +341,10 @@ def vd_lookup(id):
                   FROM vdisk_extents NATURAL JOIN vdisks
                                                 NATURAL JOIN vdisk_part
                                                 
-                  WHERE (expiry_time > datetime('now') OR not expires)
-                                    AND vdisk_extents.vdisk_id = """ + id
+                  WHERE vdisk_extents.vdisk_id = """ + id
                )
 
-    ret = cu.fetchall()
+    extent_tuples = cu.fetchall()
 
     # use this function to map the results from the database into a dict
     # list of extents, for consistency with the rest of the code
@@ -341,13 +355,204 @@ def vd_lookup(id):
     cx.commit()
     cx.close()
 
-    return map(transform, ret) # transforms the tuples into dicts to return
+    extent_dicts = map(transform, extent_tuples)
+
+    # calculate the over-allocation in sectors (happens because
+    # we allocate whole extents)
+    allocated_size = 0
+    for i in extent_dicts:
+        allocated_size += i['nr_sectors']
+
+    over_allocation = allocated_size - real_size
+
+    # trim down the last extent's length so the resulting VBD will be the
+    # size requested, rather than being rounded up to the nearest extent
+    extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation
+
+    return extent_dicts
+
+
+def vd_enlarge(vdisk_id, extra_size_mb):
+    """Create a new virtual disk.
+    vdisk_id [string]   :    ID of the virtual disk to enlarge
+    extra_size_mb  [int]:    size in megabytes to increase the allocation by
+    returns  [int]      :    0 on success, otherwise non-zero
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    extra_size = extra_size_mb * 2048
+
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id
+               + " AND (expiry_time > datetime('now') OR NOT expires)")
+    count, = cu.fetchone()
+
+    if not count: # no such vdisk
+        cx.close()
+        return -1
+
+    cu.execute("-- types int")
+    cu.execute("""SELECT SUM(extent_size)
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                         NATURAL JOIN vdisk_part
+                  WHERE vdisks.vdisk_id = """ + vdisk_id)
+
+    real_size, = cu.fetchone() # get the true allocated size
+
+    cu.execute("-- types int")
+    cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
+
+    old_size, = cu.fetchone()
+
+
+    cu.execute("--- types int")
+    cu.execute("""SELECT MAX(vdisk_extent_no)
+                  FROM vdisk_extents
+                  WHERE vdisk_id = """ + vdisk_id)
+
+    counter = cu.fetchone()[0] + 1 # this stores the extent numbers
+
+
+    # because of the extent-based allocation, the VD may already have more
+    # allocated space than they asked for.  Find out how much we really
+    # need to add.
+    add_size = extra_size + old_size - real_size
+
+    # fetch a list of extents from the expired disks, along with information
+    # about their size
+    cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
+                         vdisk_extents.part_id, extent_size
+                  FROM vdisk_extents NATURAL JOIN vdisks
+                                                  NATURAL JOIN vdisk_part
+                  WHERE expires AND expiry_time < datetime('now')
+                  ORDER BY expiry_time asc, vdisk_extent_no desc
+               """)  # aims to reuse the last extents
+                     # from the longest-expired disks first
+
+    allocated = 0
+
+    building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\
+                   + " WHERE vdisk_id = " + vdisk_id + "; "
+
+    while allocated < add_size:
+        row = cu.fetchone()
+        if not row:
+            cx.close()
+            return -1
+
+        (dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
+        allocated += extent_size
+        building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id    \
+                        + ", " + "vdisk_extent_no = " + str(counter)         \
+                        + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
+                        + " AND vdisk_id = " + str(dead_vd_id) + "; "
+
+        counter += 1
+        
+
+    # this will execute the SQL query we build to store details of the new
+    # virtual disk and allocate space to it print building_sql
+    cu.execute(building_sql)
+    
+    cx.commit()
+    cx.close()
+    return 0
+
+
+def vd_undelete(vdisk_id, expiry_time):
+    """Create a new virtual disk.
+    vdisk_id      [int]: size in megabytes for the new virtual disk
+    expiry_time   [int]: expiry time, in seconds from now
+    returns       [int]: zero on success, non-zero on failure
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id)
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return -1
+
+    cu.execute("-- types int")
+    cu.execute("""SELECT SUM(extent_size)
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                         NATURAL JOIN vdisk_part
+                  WHERE vdisks.vdisk_id = """ + vdisk_id)
+
+    real_size, = cu.fetchone() # get the true allocated size
+
+
+    cu.execute("-- types int")
+    cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
+
+    old_size, = cu.fetchone()
+
+    if real_size < old_size:
+        cx.close()
+        return -1
+
+    if expiry_time == 0:
+        expires = '0'
+    else:
+        expires = '1'
+
+    # this will execute the SQL query we build to store details of the new
+    # virtual disk and allocate space to it print building_sql
+    cu.execute("UPDATE vdisks SET expiry_time = datetime('now','"
+               + str(expiry_time) + " seconds'), expires = " + expires
+               + " WHERE vdisk_id = " + vdisk_id)
+    
+    cx.commit()
+    cx.close()
+    return 0
+
+
+
+
+def vd_list():
+    """Lists all the virtual disks registered in the system.
+    returns [list of dicts]
+    """
+    
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("""SELECT vdisk_id, size, expires, expiry_time
+                  FROM vdisks
+                  WHERE (NOT expires) OR expiry_time > datetime('now')
+               """)
+
+    ret = cu.fetchall()
+
+    cx.close()
+
+    def makedicts((vdisk_id, size, expires, expiry_time)):
+        return { 'vdisk_id' : str(vdisk_id), 'size': size,
+                 'expires' : expires, 'expiry_time' : expiry_time }
+
+    return map(makedicts, ret)
 
 
 def vd_refresh(id, expiry):
     """Change the expiry time of a virtual disk.
-    id [string]: a virtual disk identifier
-    expiry [int]: expiry time in seconds from now (0 = never expire)
+    id [string]  : a virtual disk identifier
+    expiry [int] : expiry time in seconds from now (0 = never expire)
+    returns [int]: zero on success, non-zero on failure
     """
 
     if not os.path.isfile(VD_DB_FILE):
@@ -356,6 +561,15 @@ def vd_refresh(id, expiry):
     cx = sqlite.connect(VD_DB_FILE)
     cu = cx.cursor()
 
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
+               + " AND (expiry_time > datetime('now') OR NOT expires)")
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return -1
+
     if expiry:
         expires = 1
         expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
@@ -365,17 +579,20 @@ def vd_refresh(id, expiry):
 
     cu.execute("UPDATE vdisks SET expires = " + str(expires)
                + ", expiry_time = " + expiry_ts
-               + " WHERE vdisk_id = " + id)
+               + " WHERE (expiry_time > datetime('now') OR NOT expires)"
+               + " AND vdisk_id = " + id)
 
     cx.commit()
     cx.close()
     
-    return
+    return 0
+
 
 def vd_delete(id):
-    """Deletes a Virtual Disk, making its extents available for future
-    virtual disks.
-       [id] identifier for the virtual disk to delete
+    """Deletes a Virtual Disk, making its extents available for future VDs.
+       id [string]   : identifier for the virtual disk to delete
+       returns [int] : 0 on success, -1 on failure (VD not found
+                       or already deleted)
     """
 
     if not os.path.isfile(VD_DB_FILE):
@@ -384,13 +601,46 @@ def vd_delete(id):
     cx = sqlite.connect(VD_DB_FILE)
     cu = cx.cursor()
 
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
+               + " AND (expiry_time > datetime('now') OR NOT expires)")
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return -1
+
     cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')"
                + " WHERE vdisk_id = " + id)
 
     cx.commit()
     cx.close()
     
-    return
+    return 0
+
+
+def vd_freespace():
+    """Returns the amount of free space available for new virtual disks, in MB
+    returns [int] : free space for VDs in MB
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("-- types int")
+
+    cu.execute("""SELECT SUM(extent_size)
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                           NATURAL JOIN vdisk_part
+                  WHERE expiry_time <= datetime('now') AND expires""")
+
+    sum, = cu.fetchone()
+
+    return sum / 2048
+
 
 def vd_init_db(path):
     """Initialise the VD SQLite database
@@ -461,12 +711,12 @@ def vd_extents_validate(new_extents,new_writeable):
         this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd'])
         for vbd_ext in this_vbd_extents:
             vbd_ext['writeable'] = vbd['writeable']
-            old_extents.append(vbd_ext);
-
+            old_extents.append(vbd_ext)
+            
     ##### Now scan /proc/mounts for compile a list of extents corresponding to
     ##### any devices mounted in DOM0.  This list is added on to old_extents
 
-    regexp = re.compile("/dev/(\S*) \S* \S* (..).*");
+    regexp = re.compile("/dev/(\S*) \S* \S* (..).*")
     fd = open('/proc/mounts', "r")
 
     while True: